﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
using Ivi.RFSigGen.Interop;
using Ivi.SessionFactory.Interop;
using AIRO.Devices.Solenoid;
using System.ComponentModel;
using System.Globalization;

namespace AIRO.BFSerial
{
    /// <summary>
    /// The serial state
    /// </summary>
    public enum StateType
    {
        /// <summary>
        /// Application is idle.
        /// </summary>
        Idle,
        /// <summary>
        /// Serial is running.
        /// </summary>
        Running,
        /// <summary>
        /// Serial was cancelled by user request or error occured.
        /// </summary>
        Cancelled,
        /// <summary>
        /// Serial was successfully completed.
        /// </summary>
        Completed,
        /// <summary>
        /// The program will now exit.
        /// </summary>
        Exit
    }

    

    public partial class fmMain : Form
    {
        /// <summary>
        /// 1GHz=1000000000Hz;
        /// </summary>
        private const int giga = 1000000000;

        /// <summary>
        /// Number of field steps.
        /// </summary>
        private int bCnt;
        /// <summary>
        /// Number of frequency steps.
        /// </summary>
        private int fCnt;
        /// <summary>
        /// Starting field [T]
        /// </summary>
        private double bFrom;
        /// <summary>
        /// Starting frequency [GHz]
        /// </summary>
        private double fFrom;
        /// <summary>
        /// Field increment [T]
        /// </summary>
        private double bIncr;
        /// <summary>
        /// Frequency increment [GHz]
        /// </summary>
        private double fIncr;
        /// <summary>
        /// Field after completion [T]
        /// </summary>
        private double bAfter;
        /// <summary>
        /// Number of acquisition cycles
        /// </summary>
        private long cycles;
        private long fltWnd;

        private bool keepSpectra;
        /// <summary>
        /// Magnet dwell time [sec]
        /// </summary>
        private double bDwell;
        /// <summary>
        /// Generator dwell time [sec]
        /// </summary>
        private double fDwell;
        /// <summary>
        /// Power Level
        /// </summary>
        private double powerLevel;
        ///<summary>Current value of field</summary>
        private double bCurr;
        ///<summary>Current value of frequency</summary>
        private double fCurr;
        ///<summary>Current inner loop index</summary>
        private short x;
        ///<summary>Current outer loop index</summary>
        private short y;
        ///<summary>Generator currently on</summary>
        private bool genOn;
        ///<summary>Current shot index (0,1)</summary>
        ///<remarks>We will despike pairs of shots with indexes 0 and 1.</remarks>
        private long indx;
        ///<summary>Current cycle index</summary>
        private long cycle;
        ///<summary>Total number of frames</summary>
        private long total;
        ///<summary>Frames completed</summary>
        private long completed;
        ///<summary>Experiment pending</summary>
        private bool expSheduled;
        ///<summary>Experiment running flag</summary>
        private bool expRunning;
        ///<summary>Field changing in progress</summary>
        private bool fieldRunning;
        /// <summary>
        /// Time when serial was started
        /// </summary>
        private DateTime startTime;
        /// <summary>
        /// IVI logical name of RFGenerator 
        /// </summary>
        private string rfGeneratorLogicalName;
        /// <summary>
        /// IVI logical name of Voltmeter 
        /// </summary>
        private string voltmeterLogicalName;
        /// <summary>
        /// IVI logical name of Temperature Sensor 
        /// </summary>
        private string tempSensorLogicalName;
        /// <summary>
        /// IVI logical name of Magnet Power Supply 
        /// </summary>
        private string psLogicalName;
        /// <summary>
        /// IVI RF Signal Generator
        /// </summary>
        private IIviRFSigGen rfGen;
        /// <summary>
        /// Magnet Power Supply
        /// </summary>
        private ISolenoid solenoid;
        /// <summary>
        /// IVI Session Factory
        /// </summary>
        IIviSessionFactory sf = new IviSessionFactoryClass();

        private WinSpec ws;

        private System.Timers.Timer tmr = new System.Timers.Timer();
        //private MethodInvoker runMethodInvoker;
        //private AsyncCallback wsExpCompleteCallback;



        NumberFormatInfo numericFormatProvider = new NumberFormatInfo();
        

        

        private void onSerialCompleted()
        {
            ws.ExpStore();          
            
           
           // genSwitch(false);
            if (cbBAfterCompletion.Checked)
            {
                changeField(bAfter);
            }
            State = StateType.Completed;
            /*
            rfGen.RF.OutputEnabled = false;
            rfGen.Close();
             * */
        }



        private delegate void runExpAsyncDelegate();

        
        private void run()
        {
            if ((indx == 0) && (!genOn) && (cycle == 0))
            {
                if (x == 0)
                {
                    ws.ExpInitRow();
                }
                ws.ExpInitStep();
            }
            if (InvokeRequired)
            {
                this.BeginInvoke(new runExpAsyncDelegate(ws.RunExpAsync));
            }
            else
            {
                ws.RunExpAsync();
            }
            //ws.RunExp(worker, e);
        }

        /// <summary>
        /// This method is called when serial starts
        /// </summary>
        private void start()
        {
            try
            {
                if (readParams() && frameInit())
                {                    
                    expRunning = false;
                    expSheduled = false;
                    x = 0;
                    y = 0;
                    genOn = false;
                    cycle = 0;
                    indx = 0;
                    completed = 0;
                    State = StateType.Running;
                    if (rbtnField.Checked)
                        ws.ExpInit(bCnt, fCnt, cycles, fltWnd, keepSpectra);
                    else
                        ws.ExpInit(fCnt, bCnt, cycles, fltWnd, keepSpectra);
                    changeFrequency(fFrom);
                    genSwitch(genOn);
                    rfGen.RF.Level = powerLevel;
                    changeField(bFrom);
                }
            }
            catch (Exception e)
            {
                State = StateType.Cancelled;
                MessageBox.Show("Can't start : " + e.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);

            }
        }

        
        private void onTimer(Object sender, ElapsedEventArgs e)
        {
            expSheduled = false;
            if (State == StateType.Running)
            {
                expRunning = true;

                run();                    
                /*
                run();                
                object[] pList = { null };
                this.BeginInvoke(new AsyncCallback(wsExpComplete),pList);
                 * */
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="frame"></param>
        /*
        private void wsExpComplete(IAsyncResult ar)
        {
            expRunning = false;
            onExpCompleted();

        }
        */

        
        /// <summary>
        /// This method is called after each Winspec shot to set parameters for the next Winspec experiment.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void onExpCompleted(object sender, EventArgs e)
        {
            expRunning = false;
            completed++;
            lbProgress.Text = completed.ToString();
            lbProgress.Update();
            DateTime now = DateTime.Now;            
            
            TimeSpan experimentTime = now.Subtract(startTime);
            TimeSpan duration = new TimeSpan((long)(experimentTime.Ticks * total / completed));
            DateTime completionTime = startTime.Add(duration);
            lbCompletionTime.Text = completionTime.ToString("G");
            ws.ExpProcessStep(genOn, indx);
            if (State == StateType.Running)
            {
                if (indx == 0)
                {
                    if (!genOn)
                    {
                        genOn = true;
                        genSwitch(genOn);
                    }
                    else
                    {
                        indx = 1;
                    }
                    expSheduled = false;
                    //runMethodInvoker.BeginInvoke(wsExpCompleteCallback, null);
                    //backgroundWorker.RunWorkerAsync();
                    run();
                    expRunning = true;
                }
                else
                {
                    #region indx=1
                    if (!genOn)
                    {
                        #region genOn=false
                        indx = 0;

                        cycle++;
                        if (cycle >= cycles)
                        {
                            cycle = 0;
                            ws.ExpStoreStep(x, y);
                            x++;
                            if (rbtnField.Checked)
                            {
                                #region B-Serial Step
                                if (x >= bCnt)
                                {
                                    ws.ExpStoreRow(y);
                                    x = 0;
                                    y++;
                                    if (y >= fCnt)
                                    {
                                        onSerialCompleted();
                                        return;
                                    }
                                    else
                                    {
                                        changeField(bFrom);
                                        changeFrequency(fCurr + fIncr);
                                    }
                                }
                                else
                                    changeField(bCurr + bIncr);
                                #endregion
                            }

                            else
                            {
                                #region F-Serial Step
                                if (x >= fCnt)
                                {
                                    ws.ExpStoreRow(y);
                                    x = 0;
                                    y++;
                                    if (y >= bCnt)
                                    {
                                        onSerialCompleted();
                                        return;
                                    }
                                    else
                                    {
                                        changeFrequency(fFrom);
                                        changeField(bCurr + bIncr);
                                    }
                                }
                                else
                                    changeFrequency(fCurr + fIncr);
                                #endregion
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        genOn = false;
                        genSwitch(genOn);
                    }
                    
                    if (!fieldRunning)
                    {
                        if (fDwell > 0)
                        {
                            tmr.Interval = fDwell * 1000;
                            tmr.AutoReset = false;
                            //tmr.Elapsed += onTimer;
                            expSheduled = true;
                            tmr.Start();
                        }
                        else
                        {
                            expSheduled = false;
                            //runMethodInvoker.BeginInvoke(wsExpCompleteCallback, null);
                            //backgroundWorker.RunWorkerAsync();
                            run();
                            expRunning = true;
                            
                        }
                    }
                    #endregion
                }
            }
            

        }


        private void changeField(double b)
        {
            bCurr = b;
            fieldRunning = true;
            solenoid.BeginGotoField(b);
            //tbBCurr.Text = System.Convert.ToString(b);
            //tbBCurr.Update();
        }

        private void genSwitch(bool genOn)
        {
            if (genOn)
            {
                this.pbRFStatus.Image = AIRO.BFSerial.Properties.Resources.bulb_on;                
                rfGen.RF.OutputEnabled = true;
            }
            else
            {
                this.pbRFStatus.Image = AIRO.BFSerial.Properties.Resources.bulb_off;                
                rfGen.RF.OutputEnabled = false;
            }
            pbFieldStatus.Update();
        }

        private void onFieldSet()
        {
            fieldRunning = false;
            if (State == StateType.Running)
            {
                if (completed == 0)
                {
                    startTime = DateTime.Now;
                }
                if (bDwell > 0)
                {
                    tmr.Interval = bDwell * 1000;
                    tmr.AutoReset = false;
                    //tmr.Elapsed += onTimer;
                    expSheduled = true;
                    tmr.Start();
                }
                else
                {
                    expSheduled = false;
                    //runMethodInvoker.BeginInvoke(wsExpCompleteCallback, null);
                    //backgroundWorker.RunWorkerAsync();
                    run();
                    expRunning = true;
                }
            }

        }

        private void changeFrequency(double f)
        {
            fCurr = f;
            rfGen.RF.Frequency = f * giga;            
            // setGenFrequency(f);
            tbFCurr.Text = System.Convert.ToString(f);
            tbFCurr.Update();
        }



        private bool frameInit()
        {
            total = bCnt * fCnt * cycles * 4;
            this.lbProgress.Text = "0";
            this.lbTotal.Text = total.ToString();
            lbProgress.Update();
            lbTotal.Update();
            if (total <= 0)
            {
                MessageBox.Show("Can't start - invalid number of points specified", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            return true;
        }


        private bool readParams()
        {
            try
            {
                bCnt = System.Convert.ToInt32(tbBCnt.Text);
                fCnt = System.Convert.ToInt32(tbFCnt.Text);
                bFrom = System.Convert.ToDouble(tbBFrom.Text,numericFormatProvider);
                fFrom = System.Convert.ToDouble(tbFFrom.Text,numericFormatProvider);
                bIncr = System.Convert.ToDouble(tbBIncr.Text,numericFormatProvider);
                fIncr = System.Convert.ToDouble(tbFIncr.Text,numericFormatProvider);
                fDwell = System.Convert.ToDouble(tbFDwell.Text,numericFormatProvider);
                bDwell = System.Convert.ToDouble(tbBDwell.Text,numericFormatProvider);
                powerLevel = System.Convert.ToDouble(tbPower.Text,numericFormatProvider);

                if (this.cbBAfterCompletion.Checked)
                    bAfter = System.Convert.ToDouble(this.tbBAfterCompletion.Text,numericFormatProvider);

                cycles = System.Convert.ToInt32(tbCycles.Text);
                fltWnd = System.Convert.ToInt32(tbFilterWindow.Text);
                keepSpectra = cbKeepDiffSpectra.Checked;


                voltmeterLogicalName = Properties.Settings.Default.VoltmeterLogicalName;
                rfGeneratorLogicalName = Properties.Settings.Default.RFGeneratorLogicalName;
                tempSensorLogicalName = Properties.Settings.Default.TempSensorLogicalName;
                psLogicalName = Properties.Settings.Default.PSLogicalName;
                if (rfGeneratorLogicalName == "")
                {
                    MessageBox.Show("Error! IVI RF Signal Generator Logical Name is empty! Please check Hardware Configuration!", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                rfGen = (IIviRFSigGen)sf.CreateDriver(rfGeneratorLogicalName);
                rfGen.Initialize(rfGeneratorLogicalName, false, true, "");
                rfGen.RF.DisableAllModulation();

                if (psLogicalName == "")
                {
                    MessageBox.Show("Error! IVI Power Supply Logical Name is empty! Please check Hardware Configuration!", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                solenoid = (ISolenoid)sf.CreateDriver(psLogicalName);
                ((Ivi.Driver.Interop.IIviDriver)solenoid).Initialize(psLogicalName, false, false, "");
                solenoid.StateChangedEvent += new EventHandler<StateChangedEventArgs>(solenoid_StateChangedEvent);
                solenoid.ProgressEvent += new EventHandler<ProgressEventArgs>(solenoid_ProgressEvent);
                solenoid.FieldSetEvent += new EventHandler<FieldSetEventArgs>(solenoid_FieldSetEvent);


                return true;
            }
            catch (Exception e)
            {
                MessageBox.Show("Can't start - some parameters are invalid\n" + e.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }

        void solenoid_FieldSetEvent(object sender, FieldSetEventArgs e)
        {
            tbBCurr.Text = e.Field.ToString();
            tbBCurr.Update();
            onFieldSet();
        }

        void solenoid_ProgressEvent(object sender, ProgressEventArgs e)
        {
            tbBCurr.Text = e.Field.ToString();
            tbBCurr.Update();
        }

        void solenoid_StateChangedEvent(object sender, StateChangedEventArgs e)
        {
            updateFieldState(e.State);
            if ((e.State == SolenoidState.Error)|(e.State==SolenoidState.QuenchDetected))
            {
                switch (_state)
                {
                    case StateType.Running:
                        State = StateType.Cancelled;
                        break;
                    default:
                        
                        break;
                }
            }
        }
    }
}
